using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;

namespace HalconDotNet
{
	public abstract class HVector : ICloneable, IDisposable
	{
		internal int mDimension;

		protected List<HVector> mVector;

		public int Dimension => mDimension;

		public int Length
		{
			get
			{
				if (mDimension <= 0)
				{
					return 0;
				}
				lock (mVector)
				{
					return mVector.Count;
				}
			}
		}

		public HVector this[int index]
		{
			get
			{
				if (mDimension < 1 || index < 0)
				{
					throw new HVectorAccessException("Index out of range");
				}
				AssertSize(index);
				lock (mVector)
				{
					return mVector[index];
				}
			}
			set
			{
				if (mDimension < 1 || index < 0)
				{
					throw new HVectorAccessException("Index out of range");
				}
				if (value.Dimension != mDimension - 1)
				{
					throw new HVectorAccessException("Vector dimension mismatch");
				}
				AssertSize(index);
				HVector hVector;
				lock (mVector)
				{
					hVector = mVector[index];
					mVector[index] = value.Clone();
				}
				hVector.Dispose();
			}
		}

		protected HVector(int dimension)
		{
			if (dimension < 0)
			{
				throw new HVectorAccessException("Invalid vector dimension " + dimension);
			}
			mDimension = dimension;
			mVector = ((dimension > 0) ? new List<HVector>() : null);
		}

		protected HVector(HVector vector)
			: this(vector.Dimension)
		{
			if (mDimension > 0)
			{
				mVector.Capacity = vector.Length;
				for (int i = 0; i < vector.Length; i++)
				{
					mVector.Add(vector[i].Clone());
				}
			}
		}

		[EditorBrowsable(EditorBrowsableState.Never)]
		public void TransferOwnership(HVector source)
		{
			if (source == this)
			{
				return;
			}
			if (source != null && source.Dimension != Dimension)
			{
				throw new HVectorAccessException("Vector dimension mismatch");
			}
			Dispose();
			if (source != null)
			{
				if (mDimension <= 0)
				{
					throw new HVectorAccessException("TransferOwnership not implemented for leaf");
				}
				mVector = source.mVector;
				source.mVector = new List<HVector>();
				GC.ReRegisterForFinalize(this);
			}
		}

		[EditorBrowsable(EditorBrowsableState.Never)]
		public void AssertDimension(int dimension)
		{
			if (mDimension != dimension)
			{
				throw new HVectorAccessException("Expected vector dimension " + dimension);
			}
		}

		private void AssertSize(int index)
		{
			if (mVector != null)
			{
				lock (mVector)
				{
					int count = mVector.Count;
					if (index >= count)
					{
						mVector.Capacity = index + 1;
						for (int i = count; i <= index; i++)
						{
							mVector.Add(GetDefaultElement());
						}
					}
				}
			}
		}

		protected abstract HVector GetDefaultElement();

		public HVector At(int index)
		{
			if (mDimension < 1 || index < 0 || index >= Length)
			{
				throw new HVectorAccessException("Index out of range");
			}
			lock (mVector)
			{
				return mVector[index];
			}
		}

		protected virtual bool EqualsImpl(HVector vector)
		{
			if (vector.Dimension != Dimension)
			{
				return false;
			}
			if (vector.Length != Length)
			{
				return false;
			}
			if (mDimension > 0)
			{
				for (int i = 0; i < Length; i++)
				{
					if (!this[i].VectorEqual(vector[i]))
					{
						return false;
					}
				}
			}
			return true;
		}

		public bool VectorEqual(HVector vector)
		{
			if (vector.GetType() != GetType())
			{
				return false;
			}
			return EqualsImpl(vector);
		}

		protected HVector ConcatImpl(HVector vector, bool append, bool clone)
		{
			if (mDimension < 1 || vector.Dimension != mDimension)
			{
				throw new HVectorAccessException("Vector dimension mismatch");
			}
			HVector hVector = append ? this : Clone();
			hVector.mVector.Capacity = Length + vector.Length;
			for (int i = 0; i < vector.Length; i++)
			{
				hVector.mVector.Add(clone ? vector[i].Clone() : vector[i]);
			}
			return hVector;
		}

		public HVector Concat(HVector vector)
		{
			return ConcatImpl(vector, append: false, clone: true);
		}

		[EditorBrowsable(EditorBrowsableState.Never)]
		public HVector Concat(HVector vector, bool clone)
		{
			return ConcatImpl(vector, false, clone);
		}

		public HVector Append(HVector vector)
		{
			return ConcatImpl(vector, append: true, clone: true);
		}

		[EditorBrowsable(EditorBrowsableState.Never)]
		public HVector Append(HVector vector, bool clone)
		{
			return ConcatImpl(vector, true, clone);
		}

		protected void InsertImpl(int index, HVector vector, bool clone)
		{
			if (mDimension < 1 || vector.Dimension != mDimension - 1)
			{
				throw new HVectorAccessException("Vector dimension mismatch");
			}
			if (index < 0)
			{
				throw new HVectorAccessException("Index out of range");
			}
			AssertSize(index - 1);
			lock (mVector)
			{
				mVector.Insert(index, clone ? vector.Clone() : vector);
			}
		}

		public HVector Insert(int index, HVector vector)
		{
			InsertImpl(index, vector, clone: true);
			return this;
		}

		[EditorBrowsable(EditorBrowsableState.Never)]
		public HVector Insert(int index, HVector vector, bool clone)
		{
			InsertImpl(index, vector, clone);
			return this;
		}

		protected void RemoveImpl(int index)
		{
			if (mDimension < 1)
			{
				throw new HVectorAccessException("Vector dimension mismatch");
			}
			if (index >= 0 && index < Length)
			{
				lock (mVector)
				{
					mVector[index].Dispose();
					mVector.RemoveAt(index);
				}
			}
		}

		public HVector Remove(int index)
		{
			RemoveImpl(index);
			return this;
		}

		protected virtual void ClearImpl()
		{
			if (mDimension < 1)
			{
				throw new HVectorAccessException("Vector dimension mismatch");
			}
			lock (mVector)
			{
				for (int i = 0; i < Length; i++)
				{
					mVector[i].Dispose();
				}
				mVector.Clear();
			}
		}

		public HVector Clear()
		{
			ClearImpl();
			return this;
		}

		protected abstract HVector CloneImpl();

		object ICloneable.Clone()
		{
			return CloneImpl();
		}

		[EditorBrowsable(EditorBrowsableState.Never)]
		public HVector Clone()
		{
			return CloneImpl();
		}

		protected virtual void DisposeLeafObject()
		{
		}

		public void Dispose()
		{
			GC.SuppressFinalize(this);
			if (mDimension > 0)
			{
				Clear();
			}
			else
			{
				DisposeLeafObject();
			}
		}

		public override string ToString()
		{
			if (mDimension <= 0)
			{
				return "";
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("{");
			for (int i = 0; i < Length; i++)
			{
				if (i != 0)
				{
					stringBuilder.Append(", ");
				}
				stringBuilder.Append(this[i].ToString());
			}
			stringBuilder.Append("}");
			return stringBuilder.ToString();
		}
	}
}
